#include "AssetManager.h"

CAssetManager* CAssetManager::GetInstance(void)
{
	static CAssetManager pInstance;
	return &pInstance;
}

CAssetManager::CAssetManager(void)
{
	m_bInitialized = false;
	m_pTextures.clear();
	m_pModels.clear();
	m_nNumTextures = 0;
	m_nNumModels = 0;
	m_pDev = NULL;
	m_ePoolType = D3DPOOL_DEFAULT;
}

CAssetManager::~CAssetManager(void)
{
	if(m_bInitialized)
		Shutdown();
}

bool CAssetManager::Initialize(IDirect3DDevice9* _pDev)
{
	if(m_bInitialized)
		return false;

	if(!_pDev)
	{
		MessageBoxA(NULL, "Device Was Lost", "Asset Manager Error", MB_OK);
		return false;
	}

	m_pTextures.clear();
	//m_pModels.clear();
	m_nNumTextures = 0;
	m_nNumModels = 0;

	m_pDev = _pDev;

	AddTexture("UV_Grid.dds");
// 	AddTexture("UV_Grid.dds");	// Duplicate
// 	AddTexture("UV_Grid.");		// Unsupported Extension Type
// 	AddTexture("UV_Grid");		// No Extension Type
// 	AddTexture("");				// No Name
// 	AddTexture("UV_Gri.dds");	// File Not Found
	
	// Model Error Checking
	AddModel("Tiger/tiger.x");
//  AddModel("Samus/Samus.x");	// Duplicate
//  AddModel("Samus/Samus.");	// Unsupported Extension Type
//  AddModel("Samus/Samus");	// No Extension Type
// 	AddModel("");				// No Name
// 	AddModel("Samus/Samu.x");	// File Not Found
//	AddModel("Barrel/Barrel.x");

	m_bInitialized = true;

	Reload();
	return m_bInitialized;
}

bool CAssetManager::Shutdown(void)
{
	if(!m_bInitialized)
		return false;

	map<PhTextureID, pair<string, IDirect3DTexture9*>>::iterator pTexIter = m_pTextures.begin();
	for( ; pTexIter != m_pTextures.end(); ++pTexIter)
	{
		if(pTexIter->second.second)
		{
			pTexIter->second.second->Release();
			pTexIter->second.second = NULL;
		}
	}

	m_pTextures.clear();
	m_pModels.clear();
	m_nNumTextures = 0;
	m_nNumModels = 0;
	m_pDev = NULL;
	m_ePoolType = D3DPOOL_DEFAULT;

	m_bInitialized = false;

	return !m_bInitialized;
}

bool CAssetManager::Reload(void)
{
	if(!m_bInitialized)
		return false;

	if(!m_pDev)
	{
		MessageBoxA(NULL, "Device Was Lost.", "Asset Manager Error", MB_OK);
		return false;
	}

	map<PhTextureID, pair<string, IDirect3DTexture9*>>::iterator pTexIter = m_pTextures.begin();
	for( ; pTexIter != m_pTextures.end(); ++pTexIter)
	{
		if(pTexIter->second.second)
		{
			pTexIter->second.second->Release();
			pTexIter->second.second = NULL;
		}
	}

	pTexIter = m_pTextures.begin();
	for( ; pTexIter != m_pTextures.end(); ++pTexIter)
	{
		IDirect3DTexture9* _pTexture = NULL;
		m_Error = D3DXCreateTextureFromFileEx(
			m_pDev, 
			pTexIter->second.first.c_str(),
			D3DX_DEFAULT, 
			D3DX_DEFAULT, 
			D3DX_DEFAULT, 
			0, 
			D3DFMT_UNKNOWN, 
			m_ePoolType, 
			D3DX_DEFAULT, 
			D3DX_DEFAULT, 
			0, 
			NULL, 
			NULL, 
			&_pTexture);

		if(m_Error != S_OK)
		{
			string _szTexture = "Texture Failed To Reload: " + pTexIter->second.first;
			MessageBoxA(NULL, _szTexture.c_str(), "Asset Manager Error", MB_OK);
			return false;
		}
	
		pTexIter->second.second = _pTexture;						
	}

	map<PhModelID, CBaseModel*>::iterator _pModelIter = m_pModels.begin();
	for( ; _pModelIter != m_pModels.end(); ++_pModelIter)
	{
		if(_pModelIter->second)
		{
			// Pass reloading flag;
			_pModelIter->second->Unload(true);
			if(!_pModelIter->second->Load(_pModelIter->second->GetName(), m_pDev))
			{
				string _szError = "Model Failed To Reload: " + _pModelIter->second->GetName();
				MessageBoxA(NULL, _szError.c_str(), "Asset Manager Error", MB_OK);
			}
		}
	}

	// All assets succeeded in reloading.
	return true;
}

PhTextureID CAssetManager::AddTexture(string _szTextureName)
{
	int _nTextureNameLength = _szTextureName.length();

	if(0 == _nTextureNameLength)
	{
		MessageBoxA(NULL, "Empty Texture Name", "Asset Manager Error", MB_OK);
		return PH_INVALID_TEXTURE_ID;
	}

	PhTextureID _phTextureID = 0;

	map<PhTextureID, pair<string, IDirect3DTexture9*>>::iterator _pTexIter = m_pTextures.begin();
	for( ; _pTexIter != m_pTextures.end(); ++_pTexIter)
	{
		if(_pTexIter->second.first.length() != _nTextureNameLength)
			continue;

		if(!strcmp(_pTexIter->second.first.c_str(), _szTextureName.c_str()))
		{
			MessageBoxA(NULL, "Duplicate Texture Found.", "Asset Manager Error", MB_OK);
			return PH_INVALID_TEXTURE_ID;
		}
	}

	_pTexIter = m_pTextures.begin();
	for( ; _pTexIter != m_pTextures.end(); ++_pTexIter)
	{
		if(_phTextureID == _pTexIter->first)
			++_phTextureID;
		else
			break;
	}

	if(PH_INVALID_TEXTURE_ID == _phTextureID)
	{
		MessageBoxA(NULL, "Reached Max Possible Textures.", "Asset Manager Error", MB_OK);
		return PH_INVALID_TEXTURE_ID;
	}

	if(!m_pDev)
	{
		MessageBoxA(NULL, "Device Was Lost.", "Asset Manager Error", MB_OK);
		return PH_INVALID_TEXTURE_ID;
	}

	string _szTexturePath = PH_ASSET_PATH + PH_TEXTURES_PATH + _szTextureName;

	IDirect3DTexture9* _pTexture = NULL;
	m_Error = D3DXCreateTextureFromFileEx(
		m_pDev, 
		_szTexturePath.c_str(), 
		D3DX_DEFAULT, 
		D3DX_DEFAULT, 
		D3DX_DEFAULT, 
		0, 
		D3DFMT_UNKNOWN, 
		m_ePoolType, 
		D3DX_DEFAULT, 
		D3DX_DEFAULT, 
		0, 
		NULL, 
		NULL, 
		&_pTexture);

	if(m_Error != S_OK)
	{
		string _szError = "Texture Failed To Load. Path May Not Exist: " + _szTextureName;
		MessageBoxA(NULL, _szError.c_str(), "Asset Manager Error", MB_OK);
		return PH_INVALID_TEXTURE_ID;
	}

	pair<string, IDirect3DTexture9*> _Pair(_szTexturePath, _pTexture);
	m_pTextures[_phTextureID] = _Pair;
	++m_nNumTextures;
	return _phTextureID;
}

const IDirect3DTexture9* CAssetManager::GetTexture(PhTextureID _phTextureID)
{
	// If the ID is valid
	if(_phTextureID != PH_INVALID_TEXTURE_ID)
	{
		// If the texture was found, return a pointer to it
		if(m_pTextures.find(_phTextureID) != m_pTextures.end())
			return m_pTextures[_phTextureID].second;
	}

	// If the ID was invalid or the texture was not found 
	MessageBoxA(NULL, "Invalid Texture ID Received", "Asset Manager", MB_OK);

	// Return the default texture
	return m_pTextures[0].second;
}

PhModelID CAssetManager::AddModel(string _szModelName)
{
	int _nModelNameLength = _szModelName.length();

	if(0 == _nModelNameLength)
	{
		MessageBoxA(NULL, "Empty Model Name.", "Asset Manager Error", MB_OK);
		return PH_INVALID_TEXTURE_ID;
	}

	// Find the .'s position
	int _nCounter = _szModelName.find_last_of(".");

	if(-1 == _nCounter)
	{
		MessageBoxA(NULL, "No Extension Type Provided.", "Asset Manager Error", MB_OK);
		return PH_INVALID_TEXTURE_ID;
	}

	PhModelID _phModelID = 0;

	map<PhModelID, CBaseModel*>::iterator _pModelIter = m_pModels.begin();

	for( ; _pModelIter != m_pModels.end(); ++_pModelIter)
	{
		if(_pModelIter->second->GetNameLength() != _nModelNameLength)
			continue;

		if(!strcmp(_pModelIter->second->GetName().c_str(), _szModelName.c_str()))
		{
			MessageBoxA(NULL, "Duplicate Model Found.", "Asset Manager Error", MB_OK);
			return PH_INVALID_MODEL_ID;
		}
	}

	_pModelIter = m_pModels.begin();
	for( ; _pModelIter != m_pModels.end(); ++_pModelIter)
	{
		if(_phModelID == _pModelIter->first)
			++_phModelID;
		else
			break;
	}

	// Declare the Model;
	CBaseModel* _pModel;
	EModelType _eType = nsModel::MODEL_TYPE_NONE;
	bool _bChecked = false;

	// Extension name
	string _szExtension;

	// If the . was found, store the extension name
	if(_nCounter != -1)
		_szExtension = _szModelName.substr(_nCounter, _szModelName.length());
	else
	{
		MessageBoxA(NULL, "Model Failed To Load. No Extension Type Found.", "Asset Manager Error", MB_OK);
		return PH_INVALID_MODEL_ID;
	}

	if(!strcmp(_szExtension.c_str(), ".x"))
		_eType = nsModel::MODEL_TYPE_X_MESH;
	else if(!strcmp(_szExtension.c_str(), ".phm"))
		_eType = nsModel::MODEL_TYPE_PH_MESH;
	else
	{
		MessageBoxA(NULL, "Model Failed To Load. Unsupported Extension Type Found.", "Asset Manager Error", MB_OK);
		return PH_INVALID_MODEL_ID;
	}

	// Decipher what kind of model to create
	if(nsModel::MODEL_TYPE_X_MESH == _eType)
		_pModel = new CXModel(_eType);
// 	else if(nsModel::MODEL_TYPE_PH_MESH == _eType)
//		_pModel = new CPHModel(_eType);

	// Load the mesh
	bool _bLoaded = _pModel->Load(_szModelName, m_pDev);

	// If the model failed to load for some reason
	if(!_bLoaded)
	{
		_pModel->Unload();
		delete _pModel;

		MessageBoxA(NULL, "Model Failed During Load.", "Asset Manager Error", MB_OK);
		return PH_INVALID_MODEL_ID;
	}

	_pModel->SetID(_phModelID);
	m_pModels[_phModelID] = _pModel;

	++m_nNumModels;
	return _phModelID;
}

const CBaseModel* CAssetManager::GetModel(PhModelID _phModelID)
{
	// If the ID is valid
	if(_phModelID != PH_INVALID_MODEL_ID)
	{
		// If the model was found, return a pointer to it
		if(m_pModels.find(_phModelID) != m_pModels.end())
			return m_pModels[_phModelID];
	}
	
	// If the ID was invalid or the model was not found
	//MessageBoxA(NULL, "Invalid Model ID Received.", "Asset Manager", MB_OK);

	// Return the default model
	return m_pModels[0];
}